#include "gamekit/scripting/Object.h"

#include <typeinfo>
#include <iostream>
#include <sstream>
#include <exception>

extern "C"
{
	#include "gamekit-lua/lua.h"
	#include "gamekit-lua/lualib.h"
	#include "gamekit-lua/lauxlib.h"
}

using namespace gamekit::scripting;

Object::Object(lua_State* state, uint index)
	: m_state(state),
	  m_index(index),
	  m_isNull(false)
{
	int type = lua_type(m_state, m_index);
	if(type == LUA_TNIL)
	{
		m_type = TypeNone;
	}
	else if(type == LUA_TBOOLEAN)
	{
		m_type = TypeBoolean;
	}
	else if(type == LUA_TLIGHTUSERDATA)
	{
		m_type = TypeLightUserData;
	}
	else if(type == LUA_TNUMBER)
	{
		m_type = TypeNumber;
	}
	else if(type == LUA_TSTRING)
	{
		m_type = TypeString;
	}
	else if(type == LUA_TTABLE)
	{
		m_type = TypeTable;
	}
	else if(type == LUA_TFUNCTION)
	{
		m_type = TypeFunction;
	}
	else if(type == LUA_TUSERDATA)
	{
		m_type = TypeUserData;
	}
	else if(type == LUA_TTHREAD)
	{
		m_type = TypeThread;
	}
	else
	{
		m_type = TypeNone;
	}
}

Object::Object()
	: m_state(0),
	  m_index(0),
	  m_isNull(true),
	  m_type(TypeNone)
{
}

Object::~Object()
{
}

Object Object::Null(void)
{
	return Object();
}

ObjectType Object::getType(void) const
{
	return m_type;
}

std::string Object::toString(void) const
{
	ObjectType type = getType();
	switch(type)
	{
	case TypeBoolean:
		{
			int value = lua_toboolean(m_state, m_index);
			return value == 1 ? "True" : "False";
		}
		break;
	case TypeLightUserData:
		{
			return "<LightUserData />";
		}
		break;
	case TypeNumber:
		{
			double number = lua_tonumber(m_state, m_index);
			std::ostringstream str;
			str << number;
			return str.str();
		}
		break;
	case TypeString:
		{
			const char* chars = lua_tostring(m_state, m_index);
			std::ostringstream str;
			str << chars;
			return str.str();
		}
		break;
	case TypeTable:
		{
			return "<Table />";
		}
		break;
	case TypeFunction:
		{
			return "<Function />";
		}
		break;
	case TypeUserData:
		{
			return "<TypeUserData />";
		}
		break;
	case TypeThread:
		{
			return "<Thread />";
		}
		break;
	default:
		return "<None />";
	}

	return "type";
}

bool Object::isNull(void) const
{
	return m_isNull;
}

bool Object::asBoolean() const
{
	if(getType() == TypeBoolean)
	{
		int value = lua_toboolean(m_state, m_index);
		return value == 1 ? true : false;
	}

	throw std::bad_cast();
}

double Object::asNumber() const
{
	if(getType() == TypeBoolean)
	{
		double number = lua_tonumber(m_state, m_index);
		return number;
	}

	throw std::bad_cast();
}

std::string Object::asString() const
{
	if(getType() == TypeString)
	{
		const char* chars = lua_tostring(m_state, m_index);
		return std::string(chars);
	}

	throw std::bad_cast();
}

Table Object::asTable() const
{
	if(m_type == TypeTable)
	{
	//return Table();
	}

	throw std::bad_cast();
}

Function Object::asFunction() const
{
	if(m_type == TypeFunction)
	{
		lua_CFunction func = lua_tocfunction(m_state, m_index);
		return Function(func);
	}

	throw std::bad_cast();
}

Thread Object::asThread() const
{
	if(m_type == TypeThread)
	{
		lua_State* threadState = lua_tothread(m_state, m_index);
		return Thread(threadState);
	}

	throw std::bad_cast();
}

void* Object::asUserData(void) const
{
	return lua_touserdata(m_state, m_index);
}

